#include "concurrentleppbisection.h"
#include <QtCore/QMutexLocker>
#include <QtCore/QFuture>
#include <QtCore/QList>
#include <QtConcurrentRun>
#include <iostream>
#include <QtGlobal>
#include "../../util/trianglepool.h"
#include "../../util/vertexpool.h"

#include <iostream>

ConcurrentLeppBisection::ConcurrentLeppBisection(Mesh *m,SelectionCriterion *c)
: RefinementAlgorithm(m,c) {}

ConcurrentLeppBisection::~ConcurrentLeppBisection() {
}

void ConcurrentLeppBisection::processMesh(){
	processMeshOneThreadManyTriangle();
}

void ConcurrentLeppBisection::processMeshOneThreadManyTriangle(){
	int threads = QThreadPool::globalInstance()->maxThreadCount();
	QFuture<void> futures[threads];
	for(int i=0;i<threads;i++){
		int size = (int)(work_set.size()/threads);
		if(i==0)
			int size = work_set.size()-size*(threads-1);
		futures[i] = QtConcurrent::run(this, &ConcurrentLeppBisection::processMeshPart, i*size, size);
	}

	//std::cout << "Esperando a que terminen" << std::endl;

	for(int i=0;i<threads;i++){
		futures[i].waitForFinished();
	}
}

void ConcurrentLeppBisection::processMeshPart(int offset,int size){
	QMap<Triangle *,bool>::iterator i = work_set.begin();
	i+=offset;
	QMap<Triangle *,bool>::iterator end = work_set.begin();
	end+=(offset+size);
	for(;i!=end;i++){
		mutex.lock();
		if(i.value()){
			Triangle *t = i.key();
			mutex.unlock();
			if(t->getMutex()->tryLock()){
				processTriangle(t);
			}
		} else {
			mutex.unlock();
		}
	}
}

void ConcurrentLeppBisection::processMeshOneTriangleOneThread(){
	QList< QFuture<void> > futures;
	QMap<Triangle *,bool>::iterator work_set_it;
	for(work_set_it=work_set.begin();work_set_it!=work_set.end();work_set_it++){
		futures.push_back(QtConcurrent::run(this, &ConcurrentLeppBisection::processItem, work_set_it));
	}
	QList< QFuture<void> >::iterator it;
	for(it=futures.begin();it!=futures.end();it++){
		it->waitForFinished();
	}
}

void ConcurrentLeppBisection::processItem(QMap<Triangle *,bool>::iterator &i){
	mutex.lock();
	if(i.value()){
		Triangle *t = i.key();
		mutex.unlock();
		if(t->getMutex()->tryLock())
		{
			processTriangle(t);
		}
		// si no pude obtener el lock entonces sigo inmediatamente con el siguiente,
		// significa que esta siendo refinado por un thread
	} else {
		mutex.unlock();
	}
}

Triangle * ConcurrentLeppBisection::getNeighbor(Triangle *t){
	return t->getLockedNeighbor(t->longestEdgeIndex());;
}

Vertex * ConcurrentLeppBisection::fetchNewVertex(int id, Point2F p){
	return new Vertex(id,p);
}

Triangle * ConcurrentLeppBisection::fetchNewTriangle(int id,Vertex *v1, Vertex *v2, Vertex *v3){
	return new Triangle(id,v1,v2,v3);
}

void ConcurrentLeppBisection::addVertexToMesh(Vertex *v){
	mesh->addVertex(v);
}

void ConcurrentLeppBisection::addTriangleToMesh(Triangle *t){
	mesh->addTriangle(t);
}

void ConcurrentLeppBisection::finalizeTriangle(Triangle *t){
	removeFromPending(t);
	mesh->destroyTriangle(t->getId());
}

int ConcurrentLeppBisection::getDone(){
	QMutexLocker locker(&mutex);
	return done;
}

void ConcurrentLeppBisection::removeFromPending(Triangle *t){
	QMutexLocker locker(&mutex);
	if(work_set.contains(t)){
		work_set[t] = false;
		done++;
	}
}
